package com.stu.stusystem.service.system;

import com.stu.stusystem.common.ApiException;
import com.stu.stusystem.mapper.system.DocumentMapper;
import com.stu.stusystem.model.system.Document;
import com.stu.stusystem.util.FileTypeJudgeUtil;
import com.stu.stusystem.util.UUIDUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;


/**
 * @author: cxt
 * @time: 2021/6/24
 */
@Service
@Log4j2
public class DocumentService {

    @Value("${filePath}")
    private String filePath;

    private DocumentMapper documentMapper;

    private static final Integer maxFileByteSize = 10 * 1024 * 1024;

    private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");

    // 特殊文件类型
    private static final ArrayList<String> FILETYPE = new ArrayList<>(Arrays.asList("TXT", "MD"));

    /**
     * formData保存文件
     *
     * @param file 文件
     * @return 保存后的文件id
     */
    @Transactional
    public String saveFormData(MultipartFile file) {
        long size = file.getSize();
        if (size > maxFileByteSize) {
            throw new ApiException("上传文件不能大于10MB");
        }
        String name = file.getOriginalFilename();
        String id = UUIDUtil.generate();
        if (name == null || "".equals(name)) {
            name = id + ".png";
        }
        if (name.length() > 100) {
            throw new ApiException("文件名称过长");
        }
        String[] split = name.split("\\.");
        String fileName = id + "." + split[split.length - 1];

        // 文件保存到磁盘
        String path = "";
        try {
            path = saveFile(file.getBytes(), fileName);
        } catch (IOException e) {
            log.error("保存文件出错");
        }

        // 真实的文件类型判断
        String fileType = null;
        String upperCase = split[split.length - 1].toUpperCase();
        if (FILETYPE.contains(upperCase)) {
            fileType = upperCase;
        } else {
            byte[] b = new byte[10];
            InputStream inputStream = null;
            try {
                inputStream = file.getInputStream();
                int read = inputStream.read(b, 0, 10);
                if (read != 0) {
                    fileType = FileTypeJudgeUtil.getTypeByByte(b);
                    if (fileType == null) {
                        throw new ApiException("系统不支持浏览您上传的文件");
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        // 保存到数据库
        Document document = new Document(id, name, fileType, split[split.length - 1], path, size, new Date(), 0);
        return saveDocument(document);
    }

    /**
     * 保存Base64格式
     *
     * @param file     文件Base64码
     * @param fileName 文件名称
     * @return 保存后的文件id
     */
    @Transactional
    public String saveBase64(String file, String fileName) {
        String[] split = file.split("base64,");
        if (split.length == 1) {
            throw new ApiException("编码错误");
        }
        // 图片格式
        String fileTile = split[0].substring(11, 14);
        // 实体数据
        String data = split[1];
        long size = data.length() * 3 / 4;
        String id = UUIDUtil.generate();
        String path = "";

        // 保存在磁盘
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            byte[] bytes = decoder.decodeBuffer(data);
            path = saveFile(bytes, id + "." + fileTile);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 保存到数据库
        Document document = new Document(id, fileName, fileTile.toUpperCase(), fileTile, path, size, new Date(), 0);
        return saveDocument(document);
    }

    /**
     * 数据写入磁盘
     *
     * @param data     文件数据
     * @param fileName 文件新名称
     * @return 文件存储地址
     */
    public String saveFile(byte[] data, String fileName) {
        if (data == null || data.length == 0) {
            throw new ApiException("文件数据为空");
        }
        String folder = this.format.format(new Date());
        String format = filePath + "/" + folder;
        File file = new File(format);
        if (!file.exists()) {
            if (!file.mkdirs()) {
                log.error("创建文件夹失败：{}", format);
            }
        }
        String path = format + "/" + fileName;
        try {
            Files.write(Paths.get(path), data);
        } catch (IOException e) {
            log.error("文件保存失败：{}", format);
            e.printStackTrace();
        }
        return "/file/" + folder + "/" + fileName;
    }

    /**
     * 记录到数据库
     *
     * @param document 文件对象
     * @return 保存后的文件id
     */
    public String saveDocument(Document document) {
        int insert = this.documentMapper.insert(document);
        if (insert == 0) {
            throw new ApiException(ApiException.SAVE_FAIL);
        }
        return document.getId();
    }

    /**
     * 根据id查询
     */
    public Document getDocument(String id) {
        return this.documentMapper.selectByPrimaryKey(id);
    }

    /**
     * 根据id查询多个文件
     */
    public List<Document> getDocuments(String ids) {
        String[] split = ids.split(",");
        ArrayList<String> list = new ArrayList<>(Arrays.asList(split));
        Example example = new Example(Document.class);
        example.createCriteria().andIn("id", list);
        return this.documentMapper.selectByExample(example);
    }

    /**
     * 删除id
     */
    public void remove(String id) {
        Document document = new Document();
        document.setId(id);
        document.setIsDelete(1);
        Example example = new Example(Document.class);
        example.excludeProperties().createCriteria().andEqualTo("id", id);
        int i = this.documentMapper.updateByExampleSelective(document, example);
        if (i == 0) {
            throw new ApiException(ApiException.DELETE_FAIL);
        }
    }

    /**
     * 每天23时59分清除定时清除文件
     * <p>
     * 1、文件在，记录表中没有，删除文件
     * <p>
     * 2、文件不在，记录表中存在，删除记录
     */
    @Scheduled(cron = "0 59 23 * * ?")
    public void regularRemove() {
        File[] files = new File(filePath).listFiles();
        if (files != null && files.length != 0) {
            ArrayList<String> arr = new ArrayList<>();
            for (File f : files) {
                // 查找文件夹下所有的文件
                File[] childFile = f.listFiles();
                if (childFile != null && childFile.length != 0) {
                    for (File n : childFile) {
                        String name = f.getName();
                        arr.add("/file/" + name + "/" + n.getName());
                    }
                } else {
                    if (!f.delete()) {
                        log.error("空文件夹:{}删除失败", f.getName());
                    }
                }
            }

            List<Document> all = this.documentMapper.selectAll();
            for (Document doc : all) {
                if (doc.getIsDelete().equals(1)) {
                    if (arr.contains(doc.getFilePath())) {
                        arr.remove(doc.getFilePath());
                        String path = filePath.substring(0, 2) + doc.getFilePath();
                        deleteFile(path);
                    }
                } else {
                    boolean remove = arr.remove(doc.getFilePath());
                    if (!remove) {
                        int i = this.documentMapper.deleteByPrimaryKey(doc.getId());
                        if (i == 1) {
                            log.debug("定期清理文件：文件- {} -不存在，清除记录- {} -成功", doc.getFilePath(), doc.getId());
                        } else {
                            log.error("定期清除文件出错：文件- {} -不存在，记录- {} -清除失败", doc.getFilePath(), doc.getId());
                        }
                    }
                }
            }

            // 这里 arr 存的就是文件存在，记录表中没有的，删除文件
            if (arr.size() > 0) {
                for (String s : arr) {
                    String path = filePath.substring(0, 2) + s;
                    deleteFile(path);
                }
            }
        }
        this.documentMapper.deleteByExample(new Example.Builder(Document.class)
                .where(WeekendSqls.<Document>custom().andEqualTo(Document::getIsDelete, 1)).build());
    }

    /**
     * 删除文件
     */
    public static void deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                log.debug("删除单个文件:" + fileName + "->成功！");
            } else {
                log.debug("删除单个文件:" + fileName + "->失败！");
            }
        }
    }

    @Autowired
    public void setDocumentMapper(DocumentMapper documentMapper) {
        this.documentMapper = documentMapper;
    }
}
